/* 
	Essno is an elliptic curve cryptography base software
	Popov AS, 2009
*/ 


#ifndef TYPE_ESS_H 
#define TYPE_ESS_H 


/* Предельные значения Большого числа */
#define BN_DEF_BIN	1024	/* длина БЧ в битах по умолчанию */
//#define BN_DEF_DEC	1024	/* длина БЧ в дес. зн. по умолчанию */
#define BN_MAX_BIN	(1024*8)	/* максимальная длина БЧ в битах */
//#define BN_MIN_BIN	4		/* максимальная длина БЧ в битах */
#define BN_MAX_DEC	(1024*4)	/* максимальная длина БЧ в десятичных знаках */
/*			|MaxBin| = |MaxDec| * const			*/


/*Большое число представлено в десятичной и двоичной сист. счисл.
	Десятичное: - не упакованные BCD числа   
				- в памяти растут вверх от указателя(12345: 01 02 03 04 05)
	Двоичное:	- в памяти растут вверх от указателя по 32 бита
 Указана длина каждого значения. После объявления устанавливаем одно значение = Длине, другое = 0.
 Синхронизация выполняется соотв. функцией по окончании работы с числом.
 Необходима инициализация для выделения памяти.	*/
typedef struct _BigNumber {
    unsigned char *decimal;
    unsigned int leng_dec;
    unsigned int grp_dec;
    
    unsigned int *binary;
    unsigned int leng_bin;
    unsigned int grp_bin;		// Текущая обрабатываемая группа
    unsigned int grp_bin_max;	//Кол-во групп по 32 бита c выравн. в большую сторону
} BigNumber;

typedef struct _Dot{
	BigNumber *x;
	BigNumber *y;
} Dot;

/* Макрос кол-ва групп по 32 бита c выравниванием в большую сторону в БЧ по основанию 2*/
#define MAXGR_pBN_B(tmp_BN) (int) ( ((tmp_BN)->leng_bin)/32 + 1*((((tmp_BN)->leng_bin)%32) ? 1 : 0) ) 
/*Зависимость длины Десятичной от Двоичной (и наоборот)
 *			|MaxBin| = |MaxDec| * const 						
 *Определяем кол-во групп памяти по основанию (DEC, BIN) 
 *  в зависимости от известной длины другого основания basis    + для переноса		*/
#define gMEM_FOR_DEC(BN, basis) (int) ( basis==2 ? 	\
												(BN->leng_bin) / (BN_MAX_BIN/BN_MAX_DEC) + 4 \
												: (BN->leng_dec) + 4 )
#define gMEM_FOR_BIN(BN, basis) (int) ( basis==10 ? \
												(BN->leng_dec) * (BN_MAX_BIN/BN_MAX_DEC) + 1 \
												: MAXGR_pBN_B(BN) + 1 ) 
/*Вывод на экран содержания БЧ*/
#define PRINT_BN(BN) printf("\nBN->leng_dec=%d, BN->grp_dec=%d, BN->leng_bin=%d, BN->grp_bin=%d, BN->grp_bin_max=%d\n             BN->decimal[0]=%x, BN->binary[0]=%x \n              gMEM_FOR_DEC(BN, 2)=%d, gMEM_FOR_DEC(BN, 10)=%d, gMEM_FOR_BIN(BN, 2)=%d, gMEM_FOR_BIN(BN, 10)=%d\n", \
								BN->leng_dec, BN->grp_dec, BN->leng_bin, BN->grp_bin, BN->grp_bin_max, BN->decimal[0], BN->binary[0],  gMEM_FOR_DEC(BN, 2), gMEM_FOR_DEC(BN, 10),  gMEM_FOR_BIN(BN, 2), gMEM_FOR_BIN(BN, 10) );
#define PRINT_BN_DEC(BN) do{			\
							int nn=0;								\
							for(nn=BN->leng_dec; nn>0; nn--)		\
								printf("%d", BN->decimal [nn-1] );	\
						}while(0)

#define PRINT_BN_BIN(BN) do{			\
							int nn=0;								\
							for(nn=BN->grp_bin_max; nn>0 ; nn--)	\
								printf(".%8x", BN->binary [nn-1] );	\
						}while(0);
/*Создание нового БЧ*/
#define NEW_BN( BN, pBN, l_bit, l_dec) BigNumber BN;			\
										BigNumber *pBN=&BN;		\
										pBN->leng_bin = l_bit;	\
										pBN->leng_dec = l_dec;	\
										initBN( pBN);
/*Создание копии БЧ*/
#define INIT_BN_BN_DEC(D_BN, S_BN) 		D_BN->leng_dec = S_BN->leng_dec;	\
										D_BN->leng_bin = 0;					\
										initBN( D_BN);						\
										memcpy( D_BN->decimal, S_BN->decimal, S_BN->leng_dec);
/*Присвоить БЧ_1 := БЧ_2*/
#define EQ_BN_BN_DEC( D_BN, S_BN) D_BN->leng_dec = S_BN->leng_dec;\
								   memcpy( D_BN->decimal, S_BN->decimal, S_BN->leng_dec);
/*Убирает старшие нули для чисел вида: 00123456*/
#define WITHOUT_0_BN_DEC(BN) 	while(BN->decimal [BN->leng_dec -1] == 0 && (BN->leng_dec) >1 ){\
										BN->leng_dec -=1; };
   
				
#define NULL_BN_DEC( BN) memset(BN->decimal, 0x00, BN->leng_dec);   BN->leng_dec =1;
#define NULL_BN_GLOB_DEC( BN) memset(BN->decimal, 0x00, BN_MAX_DEC);   BN->leng_dec =1;

/* Инициализация и удаление Большого числа (выделение/удаление памяти в куче)*/
int initBN(BigNumber *BN);
int clearBN(BigNumber *BN);
/*Выделение заданного ко-ва байт в памяти с проверкой и обнулением*/
void * ess_malloc( u_int leng_b);
/*Загрузка массива цифр заданной длины (в прямом порядке)*/
int loadBN_decimal(BigNumber *BN, char *mas_d, int size_d);
int loadBN_binary(BigNumber *BN, char *mas_d, int size_d );
/*Синхронизация Большого числа по основанию (2, 10) - (другое зависит от него) */
int synchBN(BigNumber *BN, int basis);
/*Алгоритмы перевода числа в другую систему счисления*/
int	synchBN_2_to_10(BigNumber *BN);
int	synchBN_10_to_2(BigNumber *BN);

/*Арифметические операции с БЧ*/
/*ВНИМАНИЕ: следить за |D_BN| - размерностью приёмника*/

/*Прибавление к БЧ1 БЧ2 по смещению sh: D_BN = D_BN + S_BN*10^sh*/ 
int addBN_BN_dec(BigNumber *D_BN, BigNumber *S_BN, u_int sh);
/*Умножение БЧ по основанию 10 и добавление числа*/
int muladdBN_dec(BigNumber *BN, u_int nmul, u_int nadd);
/*Деление десятков БЧ на ndiv(2) и возвращение остатка (-1 при окончании)*/
int divBN_dec(BigNumber *BN, u_int ndiv);
/*Большее из БЧ_1 и БЧ_2 (0- БЧ_1=БЧ_2;   1- БЧ_1<БЧ_2,   2- БЧ_1>БЧ_2 )*/
int maxBN_BN_dec(BigNumber *BN_1, BigNumber *BN_2);
/*Вычитание из БЧ_1 - БЧ_2  и флаг акселератора*/
int subBN_BN_dec(BigNumber *BN_1, BigNumber *BN_2, int acc);
/*Умножение одного БЧ на другое, с выводом в D_BN*/
int mulBN_BN_dec(BigNumber *D_BN, BigNumber *BN_1, BigNumber *BN_2);
/* Взятие модуля:  D_BN=S_BN mod( M_BN) */
int modBN_BN_dec(BigNumber *D_BN, BigNumber *S_BN, BigNumber *M_BN);
/*Число вхождений в число A числа B (деление с остатком) (Q-множитель, R-остаток): A=BQ+R   (можно над одним)*/  
int divBN_BN_dec(BigNumber *A, BigNumber *B, BigNumber *Q, BigNumber *R);
/*Расширенный алгоритм Евклида: Ax+By=D*/
int euclidBN_BN_dec(BigNumber *A, BigNumber *B, BigNumber *X, BigNumber *Y, BigNumber *D);


/*Арифметические операции с ТОЧКАМИ*/
/*Сложение двух точек и получение третьей*/
int sumDot_Dot( Dot *D, Dot *Q_1, Dot *Q_2, BigNumber *p, BigNumber *a);
/*Быстрое умножение точки на БЧ: Q=kP */
int mulBN_Dot_dec( Dot *Q, BigNumber *k, Dot *P, BigNumber *p, BigNumber *a);
/*Умножение точки на БЧ*/
#define MUL_BN_DOT_DEC( DDot, BN, Dot, p, a) 		/*Первое сложение*/							\
													sumDot_Dot( DDot, Dot, Dot, p, a);			\
													subBN_BN_dec( BN, ONE_BN, 0);				\
													subBN_BN_dec( BN, ONE_BN, 0);		/* k-2*/\
													/*Прибавляем остальные P к точке DDot*/		\
													while( maxBN_BN_dec( BN, NULL_BN)!=0 ){		\
														sumDot_Dot( DDot, DDot, Dot, p, a);		\
														subBN_BN_dec( BN, ONE_BN, 0); /* k--*/	\
													};




#endif 